import sys
import bluetooth
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, 
                            QPushButton, QTextEdit, QListWidget, QLabel, 
                            QWidget, QComboBox, QLineEdit, QMessageBox)
from PyQt6.QtCore import Qt, QThread, pyqtSignal

class BluetoothScanner(QThread):
    """
    扫描蓝牙设备进程
    :param QThread: _description_
    """
    devices_found = pyqtSignal(list)
    scan_failed = pyqtSignal(str)
    
    def run(self):
        try:
            print("正在扫描蓝牙设备...")
            devices = bluetooth.discover_devices(lookup_names=True, duration=8, flush_cache=True)
            self.devices_found.emit(devices)  #
        except Exception as e:
            self.scan_failed.emit(str(e))

class BluetoothConnectionThread(QThread):
    """
    连接蓝牙设备进程
    :param QThread: _description_
    """
    data_received = pyqtSignal(str)
    connection_error = pyqtSignal(str)
    
    def __init__(self, sock):
        super().__init__()
        self.sock = sock
        self._is_running = True
    
    def run(self):
        """
        运行进程
        """
        try:
            while self._is_running:
                data = self.sock.recv(1024)
                if data:
                    self.data_received.emit(data.decode('utf-8'))
        except Exception as e:
            print(self.connection_error)
            self.connection_error.emit(str(e))  # 提示退出设备连接。
    
    def stop(self):
        """
        结束进程，改变进程的连接状态
        """
        self._is_running = False

class MainWindow():
    def __init__(self):
        from PyQt6.uic.load_ui import loadUi
        from PyQt6.QtWidgets import QMainWindow
        
        # 蓝牙连接相关变量
        self.bt_socket = None
        self.bt_thread = None
        self.selected_device: str = ''  # 选中设备的mac地址
        
        self.local_mac = MainWindow.get_local_mac()  # 本地mac
        ui: QMainWindow = loadUi('main.ui') # type: ignore
        self.ui: QMainWindow = ui
        self.device_list = ui.device_list
        self.device_list.itemClicked.connect(self.select_device)

        self.scan_bt_btn = ui.scan_bt_btn

        self.scan_bt_btn.clicked.connect(self.scan_bluetooth_devices)

        self.received_data = ui.received_data
        self.received_data.setReadOnly(True)

        self.connect_btn = ui.connect_btn
        self.connect_btn.clicked.connect(self.connect_device)
        self.connect_btn.setEnabled(False)

        self.disconnect_btn = ui.disconnect_btn
        self.disconnect_btn.clicked.connect(self.disconnect_device)
        self.disconnect_btn.setEnabled(False)

        self.send_btn = ui.send_btn
        self.send_btn.clicked.connect(self.send_data_to_device)
        self.send_btn.setEnabled(False)

        self.status_label:QLabel = ui.status_label
        self.status_label = ui.status_label
        self.status_label.setText(f"状态: 未连接")

        self.send_data = ui.send_data

        self.clear_btn = ui.clear_btn
        self.clear_btn.clicked.connect(self.clear_received_data)

        self.mac_label: QLabel = ui.mac_label
        self.mac_label.setText(f"本机MAC: {self.local_mac}")
        # self.mac_label.setStyleSheet("color: #666666; font-size: 10pt;")

        ui.setWindowTitle("蓝牙通信工具(PyBluez)")
        ui.setGeometry(100, 100, 800, 600)
        self.scan_bluetooth_devices()  # 默认先扫描一次周围的蓝牙设备
    
    @staticmethod
    def get_local_mac() -> str:
        """
        获取本地mac地址
        :return: 返回获取的
        """
        mac = ''
        try:
            mac = bluetooth.read_local_bdaddr()[0]  # 使用pybluez直接获取mac
        except:
            try:
                mac =  bluetooth.discover_devices(lookup_names=True)[0][0]  # 获取第一个可用蓝牙适配器的地址
            except:
                mac = "无法获取"
        print('mac = ', mac)
        return mac

    def scan_bluetooth_devices(self):
        """
        扫描蓝牙设备
        """
        def update_bluetooth_devices(devices):
            """
            更新蓝牙设备，并统计到软件中。
            :param devices: 设备列表。mac地址 设备名称。
            """
            self.device_list.clear()
            if not devices:
                self.device_list.addItem("未找到蓝牙设备")
                return
            for addr, name in devices:
                self.device_list.addItem(f"{name} ({addr})")
            self.connect_btn.setEnabled(True)

        self.device_list.clear()
        self.device_list.addItem("扫描中...")
        self.scan_bt_btn.setEnabled(False)
        
        self.scanner = BluetoothScanner()  # 创建扫描蓝牙设备进程
        self.scanner.devices_found.connect(update_bluetooth_devices)
        self.scanner.scan_failed.connect(self.show_error)  # 捕获异常时会调用
        self.scanner.finished.connect(lambda: self.scan_bt_btn.setEnabled(True))
        self.scanner.start()
    
    
    def select_device(self, item):
        """
        选择设备，获取目标设备的mac地址。
        :param item: str，设备名称(mac)。
        """
        self.selected_device = item.text().split("(")[-1][:-1]
    
    def connect_device(self):
        """
        连接选中的设备。
        """
        if not hasattr(self, 'selected_device'):
            QMessageBox.warning(None, "警告", "请先选择要连接的设备")
            return
        common_channels = range(20)  # 常见RFCOMM通道号（按优先级排序）1=A2DP/SPP, 6=AVRCP, 13=OBEX
        for channel in common_channels:
            try:
                print(f"尝试连接 {self.selected_device}（通道 {channel}）...")
                self.bt_socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
                self.bt_socket.connect((self.selected_device, channel))
                print(f"✅ 连接成功！通道 {channel}")
                
                # 更新UI状态
                self.received_data.append(f"已连接到 {self.selected_device}（通道 {channel}）")
                self.connect_btn.setEnabled(False)
                self.disconnect_btn.setEnabled(True)
                self.send_btn.setEnabled(True)
                self.status_label.setText('状态：已连接')

                # 启动接收线程
                self.bt_thread = BluetoothConnectionThread(self.bt_socket)
                self.bt_thread.data_received.connect(self.update_received_data)
                self.bt_thread.connection_error.connect(self.show_error)  # 
                self.bt_thread.start()
                break
                
            except (bluetooth.BluetoothError, OSError) as e:
                print(f"❌ 连接失败（通道 {channel}）: {e}")
                if self.bt_socket:
                    self.bt_socket.close()
                self.bt_socket = None
        else:
            print("❌ 所有通道均尝试失败！")
    
    def disconnect_device(self):
        """
        取消连接设备。
        """
        if self.bt_thread:
            self.bt_thread.quit()
            self.bt_thread.wait(5)
            self.bt_thread.stop()  # 蓝牙连接进程停止

        if self.bt_socket:
            self.bt_socket.close()
        
        self.received_data.append("已断开连接")
        self.connect_btn.setEnabled(True)
        self.disconnect_btn.setEnabled(False)
        self.send_btn.setEnabled(False)
        self.status_label.setText('状态：未连接')
    
    def send_data_to_device(self):
        """
        以utf-8的编码方式，发送数据到设备。
        """
        if not self.bt_socket:
            self.show_error("未连接到设备")
            return
        
        data = self.send_data.text()
        try:
            self.bt_socket.send(data.encode('utf-8'))
            self.received_data.append(f"发送: {data}")
            self.send_data.clear()
        except Exception as e:
            self.show_error(f"发送失败: {str(e)}")
    
    def update_received_data(self, data):
        """
        接收数据。
        :param data: 需要接收的数据。
        """
        self.received_data.append(f"接收: {data}")
    
    def clear_received_data(self):
        """
        清除已接收的数据。
        """
        self.received_data.clear()
    
    def show_error(self, message):
        """
        提示出现的错误，包括中断设备连接、数据发送失败、未连接到设备。同时发送在软件对话框中。
        :param message: 错误信息描述。
        """
        QMessageBox.critical(None, "错误", message)
        self.received_data.append(f"错误: {message}")
    
    def closeEvent(self, event):
        """
        关闭事件。
        :param event: 关闭事件描述。
        """
        self.disconnect_device()  # 断开设备连接
        event.accept()
        print('窗口关闭')


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.ui.show()
    sys.exit(app.exec())
    